package org.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.*;
import org.springframework.core.convert.ConversionService;

import java.lang.reflect.Method;

/**
 * @author derekyi
 * @date 2020/11/22
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {

	private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();

	@Override
	protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {

		//先问问你是否bean需要代理，就看看你有没有可以代理的对象。涉及了AOP
		//一般代理对象无法填充属性，所以我们在这里做操作
		Object bean = resolveBeforeInstantiation(beanName, beanDefinition);

		if (bean != null) {

			//这个bean为空走下面的逻辑！！！
			return bean;//如果这个bean不为空就返回了
		}
		//如果不需要代理直接创建，我们进入看看如何创建
		return doCreateBean(beanName, beanDefinition);
	}

	/**
	 * 执行InstantiationAwareBeanPostProcessor的方法，如果bean需要代理，直接返回代理对象
	 *
	 * @param beanName
	 * @param beanDefinition
	 * @return
	 */
	protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
		//代理对象无非就是jdk或者cglib代理对象帮忙创建，并且帮忙增强
		//传入beanDefinition里的类，和名称。看看里面的类如何判断

		//返回的是动态代理类，这个方法里会创建动态代理类
		Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);


		if (bean != null) {
			//如果bean不为空。创建成功，执行下面方法，那刚创建的bean存入
			bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
		}
		return bean;
	}


	protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) {
		//我们前后置对象beanPostProcessor，在new ioc容器的时候已经都创建出来了

		//为什么代理对象创建的时候会有beanPostProcessor出现？
		//beanPostProcessor是对bean进行操作和修改的，也可以做到替换bean。

		//可以说，他（beanPostProcessor这个类）是AOP融入IOC（bean实例化）的原理
		for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
			//这里判断beanPostProcessor（）是不是InstantiationAwareBeanPostProcessor类型（继续类型判断）
			if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {

				//这里beanPostProcessor如果是InstantiationAwareBeanPostProcessor的子类
				//调用前置方法postProcessBeforeInstantiation（我们接下来看看他的前置方法长什么样子。源码不一定有，看视频的）
				Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).
						postProcessBeforeInstantiation(beanClass, beanName);
				if (result != null) {
					//在这里和之前有不同，之前result不会为空（前置方法里一般创建动态代理的对象，如果没有返回说明走其他逻辑）
					return result;

				}
			}
		}
		//走返回null逻辑就会进行属性填充
		return null;
	}

	protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
		Object bean;

		try {
			//最后的bean对象就是在这个方法里创建的，我们进去看看，在这里就创建出来bean的对象了
			//这里实例化出来，里面的属性还是null
			//通过反射构造方法实例化一个bean
			bean = createBeanInstance(beanDefinition);

			//为了解决循环依赖问题，将实例化后的bean放进缓存中提前暴露（123级map里）

			if (beanDefinition.isSingleton()) {//这里先判断这个bean是不是一次性的（单例），默认true往下走
				Object finalBean = bean;
				//这个方法是将代理对象添加进入三级缓存，代理对象的生成在下面的方法里
				addSingletonFactory(beanName, new ObjectFactory<Object>() {
					//new ObjectFactory<Object>()，这是个匿名内部了，他重写了getObject方法
					@Override
					public Object getObject() throws BeansException {
						//getEarlyBeanReference是创建代理对象的方法
						return getEarlyBeanReference(beanName, beanDefinition, finalBean);
					}
				});
			}
			//下面这些操作，其实就是bean的生命周期的事情比如
			// 1.bean实例化之后做的
			//2.允许BeanPostProcessor修改属性值
			//3.为bean填充属性
			//4.执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
			//5.注册有销毁方法的bean


			//我们之前代理对象的属性无法填充，原因就是我们不走上面的逻辑，而是走下面这个方法逻辑
			//这个方法和代理对象的属性填充有关
			//实例化bean之后执行，你是否要给当前bean设置属性，需要true，不用false。
			boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
			if (!continueWithPropertyPopulation) {
				//不需要直接返回
				return bean;
			}//如果是ture，说明需要填充属性，走下面方法


			//1.@Value，@Autowired
			//在设置bean属性之前，允许BeanPostProcessor修改属性值（因为处理Value的类实现的接口是BeanPostProcessor）
			applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);


			//为bean填充属性，给bean填充属性的具体操作就在这里面
			//这里di注入，我们进入
			applyPropertyValues(beanName, bean, beanDefinition);


			//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
			//我们进入初始化方法，第6集，初始化方法和销毁
			bean = initializeBean(beanName, bean, beanDefinition);



		} catch (Exception e) {
			throw new BeansException("Instantiation of bean failed", e);
		}

		//注册有销毁方法的bean，是注册，不是执行bean销毁方法。
		registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
		//传入bean，beanname，beanDefinition
		Object exposedObject = bean;

		//这里又做了一下判断这bean是不是单例的，在单例集合里找到他beandefinition对象
		if (beanDefinition.isSingleton()) {//看beanDefinition是否是单例（看beanDefinition是bean的描述）
			//如果有代理对象，此处获取代理对象
			exposedObject = getSingleton(beanName);
			addSingleton(beanName, exposedObject);
			//他的父类DefaultSingletonBeanRegistry，单例bean注册的方法addSingleton，
			// 把这个类存入一级缓存，顺便把二三级缓存删除
		}
		return exposedObject;
	}

	protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {

		Object exposedObject = bean;
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			//实现了InstantiationAwareBeanPostProcessor接口的类，有能力生成代理对象（InstantiationAwareBeanPostProcessor是特殊的前后置类，下面的方法就是他特殊的方法）
			//InstantiationAwareBeanPostProcessor

			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				exposedObject = ((InstantiationAwareBeanPostProcessor) bp).
						//特殊的前后置方法的单独的方法
						getEarlyBeanReference(exposedObject, beanName);

				if (exposedObject == null) {
					return exposedObject;
				}
			}
		}

		return exposedObject;
	}

	/**
	 * bean实例化后执行，如果返回false，不执行后续设置属性的逻辑
	 *
	 * @param beanName
	 * @param bean
	 * @return
	 */
	private boolean applyBeanPostProcessorsAfterInstantiation(String beanName, Object bean) {
		//里面做了个判断，是否需要填充属性（因为代理对象的beandefinition里的属性也没有赋值）
		//如果这里不做处理，给beandefinition填充属性，后面给bean赋值里面都没有属性
		//这里就是判断，并且给给beandefinition填充属性（提前处理一下），里面的属性是xml里读取的
		boolean continueWithPropertyPopulation = true;
		for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
			if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
				//InstantiationAwareBeanPostProcessor，代理对象实现的接口，里面的后置方法在这个地方执行！

				//执行后置方法，我们后置方法返回值定义booler，
				if (!((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessAfterInstantiation(bean, beanName)) {
					continueWithPropertyPopulation = false;
					break;
				}
			}
		}
		return continueWithPropertyPopulation;
	}

	/**
	 * 在设置bean属性之前，允许BeanPostProcessor修改属性值
	 *
	 * @param beanName
	 * @param bean
	 * @param beanDefinition
	 */
	protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {


		//终于，你这个sb方法原来干这事情的啊
		for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
			if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
				//类型匹配，所有实现了InstantiationAwareBeanPostProcessor
				//他执行的postProcessPropertyValues方法就是文档里写的
				//传入参数，beanDefinition里的所有属性的集合，自己的bean实例化对象
				PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).
						postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);
						//这一这个方法！这个方法不是前后置方法，是InstantiationAwareBeanPostProcessor实现类特有的
						//这个方法有两个实现类

						//1.是用于处理@Value，@Autowired注解
						//2.用于处理代理对象的beandefinition的属性填充的

				if (pvs != null) {
					for (PropertyValue propertyValue : pvs.getPropertyValues()) {
						//这里把这个属性添加进入beanDefinition。在后序处理里，把beanDefinition里属性拿出来填入bean
						beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
					}
				}
			}
		}
	}

	/**
	 * 注册有销毁方法的bean，即bean继承自DisposableBean或有自定义的销毁方法
	 *
	 * @param beanName
	 * @param bean
	 * @param beanDefinition
	 */
	protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
		//只有singleton类型bean会执行销毁方法
		//1.如果从xml读取的beanDefinition发现他不是单例的，就不会执行（销毁方法需要看是否是单例）
		if (beanDefinition.isSingleton()) {

			//如果当前这个bean又实现了DisposableBean这个接口或者能在beanDefinition（xml文件里定义了）销毁方法
			//二者有其一，都会进行注册
			//也就是需要有销毁方法，销毁方法定义就这两种，如果都不属于就不会执行销毁方法
			if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
				//注册，先存入beanName，把一些属性封装在DisposableBeanAdapter对象（bean），然后存入一个有销毁方法的bean的map集合
				registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
				//那么bean是什么时候执行销毁方法呢？看DisposableBeanAdapter构造方法里，有一个destory、方法

			}
		}
	}

	/**
	 * 实例化bean
	 *
	 * @param beanDefinition
	 * @return
	 */
	protected Object createBeanInstance(BeanDefinition beanDefinition) {
		//继续进入
		return getInstantiationStrategy().instantiate(beanDefinition);
	}

	/**
	 * 为bean填充属性
	 *
	 * @param bean
	 * @param beanDefinition
	 */
	protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
		try {
			//在这个方法里面他会进行循环，beanDefinition.getPropertyValues().getPropertyValues()进行的循环
			//PropertyValues他本身是一个对象，里面的属性是一个集合，元素是getPropertyValue
			//所有的元素就维护再里面了
			//循环赋值
			for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
				String name = propertyValue.getName();
				Object value = propertyValue.getValue();


				//判断是否关联另外一个bean
				if (value instanceof BeanReference) {//这里会进行一个类型匹配，是否你的属性类型是BeanReference
					//这个属性类型就是说他是一个含有属性依赖的对象类型

					// beanA依赖beanB，先实例化beanB，person对象依赖于car对象，我们这里先实例化car
					BeanReference beanReference = (BeanReference) value;
					//覆盖
					value = getBean(beanReference.getBeanName());

				} else {
					//如果没有类型依赖直接存
					//类型转换，通过反射得到value的类型，然后最强转
					Class<?> sourceType = value.getClass();
					//得到value的类型type，这里是String类型的

					//targetType（目标类型）name是实例里的属性名称，bean.getClass()是创建出来的bean的字节码。
					//这个bean里的叫做name的属性的类型，也就是我们目标类型，就是我们要把PropertyValue转换成的最终类型
					//这里我们用反射，先得到bean的类，再对类反射得到属性的类型，进行类型转换
					Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);
					//之前我们只学习了要类型，转换，现在知道如何类型转换了


					//通过这个类进行类型转换了，sourceType--->targetType(源类型和目标类型)
					ConversionService conversionService = getConversionService();
					//getConversionService，眼熟吧，就是返回ioc的conversionService对象（成员变量）

					if (conversionService != null) {
						//判断是否可以转换，就是之前学的方法（pair为key，value是类型转换器。在里面的map集合判断）
						if (conversionService.canConvert(sourceType, targetType)) {
							//我们要把sourceType类型转换成targetType，这里都是String，不需要转换。如下
							value = conversionService.convert(value, targetType);
						}
					}
				}

				//最后通过反射设置属性，把属性设置进去
				BeanUtil.setFieldValue(bean, name, value);
			}
		} catch (Exception ex) {
			throw new BeansException("Error setting property values for bean: " + beanName, ex);
		}

	}

	protected Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
		//这里是BeanFactoryAware（还有一个ApplicationContextAware,在前置方法那块）
		if (bean instanceof BeanFactoryAware) {
			//如果你这个bean实现了这个BeanFactoryAware接口
			//直接调用bean里面的setBeanFactory方法，this是本身，本身是一个ioc容器（基本的那个）
			((BeanFactoryAware) bean).setBeanFactory(this);
			//这个set方法本身需要传入一个ioc容器（这个this就是ioc容器的父亲接口）
		}

		//执行BeanPostProcessor的前置处理，前置处理（前置处理里面还有一个ApplicationContextAware的方法）
		Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

		try {
			//初始化方法（这里执行类型转换器的初始化）
			invokeInitMethods(beanName, wrappedBean, beanDefinition);
			//初始化方法，进入
		} catch (Throwable ex) {
			throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", ex);
		}

		//执行BeanPostProcessor的后置处理，后置处理
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		return wrappedBean;
	}

	@Override
	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {
		//传入bean，未初始化的bean。
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			//所有的前置方法都运行一次，这里还涉及aware的接口方法，我们进入方法查看
			Object current = processor.postProcessBeforeInitialization(result, beanName);//result是传入的bean
			if (current == null) {
				return result;
			}
			result = current;//重复赋值给bean
		}
		return result;
	}

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {
		//看看干了啥
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			//就是一些后置方法，所有的后置bean，循环判断。
			//我们可以在这里对bean做一些初始化方法（后置里做初始化，我也不理解），我们在里面可以类型判断
			//如果是代理类，就****，否则****。前后置方法和beandefinition的操作，一定要做类型判断，不然全操作了
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;//返回
			}
			result = current;
		}
		return result;
	}

	/**
	 * 执行bean的初始化方法
	 *
	 * @param beanName
	 * @param bean
	 * @param beanDefinition
	 * @throws Throwable
	 */
	protected void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Throwable {
		//这里直接实现接口的初始化方法，所以可以看出顺序是先执行接口的方法

		if (bean instanceof InitializingBean) {//判断是不是实现了那个接口的方法
			//InitializingBean类重写了afterPropertiesSet
			//ConversionServiceFactoryBean是他的实现类（类型转换器bean）
			((InitializingBean) bean).afterPropertiesSet();//直接执行方法
		}

		//后面这一端的逻辑就是通过反射实现初始化方法（也就是自定义的初始化方法，xml文件定义的，只不过通过反射实现）（所以顺序就是这个原因）
		String initMethodName = beanDefinition.getInitMethodName();//从beanDefinition读出初始化方法（xml文件里）
		if (StrUtil.isNotEmpty(initMethodName) && !(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName))) {
			Method initMethod = ClassUtil.getPublicMethod(beanDefinition.getBeanClass(), initMethodName);
			if (initMethod == null) {
				throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
			}
			initMethod.invoke(bean);//反射，把bean传入
		}
	}

	public InstantiationStrategy getInstantiationStrategy() {
		return instantiationStrategy;
	}

	public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
		this.instantiationStrategy = instantiationStrategy;
	}
}
